home *** CD-ROM | disk | FTP | other *** search
/ Belgian Amiga Club - ADF Collection / BS1 part 41.zip / BS1 part 41 / Compute`s Amiga resource 1.adf / Source / ALC / advlaser.c < prev    next >
C/C++ Source or Header  |  1989-02-07  |  64KB  |  1,981 lines

  1. /* Amiga Advanced Laser Chess [9/26/88]  Last modified: 11/2/88 */
  2. /* Mike M. Duppong   for   Compute's Amiga Resource */
  3. /* Aztec C V3.6a */
  4.  
  5. #include "advlaser.h"
  6. #include "advsound.h"
  7. #include <stdio.h>
  8.  
  9. /* External variables */
  10. extern struct Window *window;         /* From ADVGRAPH.C */
  11. extern struct Screen *screen;         /* ADVGRAPH.C */
  12. extern struct BitMap *current_bitmap; /* ADVGRAPH.C */
  13. extern struct BitMap *image_bitmap;   /* ADVGRAPH.C */
  14. extern struct BitMap backup_bitmap;   /* ADVGRAPH.C */
  15. extern struct IntuitionBase *IntuitionBase; /* From ADVDOOR.C */
  16. extern struct GfxBase *GfxBase;       /* ADVDOOR.C */
  17.  
  18. /* Local variables */
  19. void *allocate_mem();        /* Custom memory allocation routine */
  20. int moves_left;              /* Number of moves remaining in turn */
  21. int moves_taken;             /* Number of moves taken so far in turn */
  22. ULONG beam_hit();
  23. ULONG grid[GRIDX][GRIDY];    /* Contains a 32-bit ID for each grid position */
  24. int gate[4];                 /* Gate flags - OPEN or CLOSED */
  25. struct explosion_struct explode_info[MAXEXPLOSIONS];
  26. int selected_x, selected_y;  /* Coordinates of currently-selecte piece */
  27. int selected_f;              /* Facing of piece when selected */
  28. int gic[] = {1, 3, 7, 9};    /* Gate index y-coordinate conversions */
  29. int xreflect[] = {0, -1, -1, -1, 0, 1, 1, 1}; /* Beam reflect directions according */
  30. int yreflect[] = {1, 1, 0, -1, -1, -1, 0, 1}; /* to facing */
  31. int split[8][4];              /* Keeps track of beam splits */
  32. int split_index;              /* Current split index */
  33. int ident_split[8][2];        /* Tracks all splitters hit to avoid split redundancy */
  34. int split_max;                /* Tracks the maximum number of splits that occur */
  35. int cas_list[32][2];          /* Bomb casualty list- 32 pieces can be destroyed MAX at once by all 4 bombs going off */
  36. int cas_index;                /* Index of casualties */
  37. int bomb_hit_list[4][2];      /* Tracks which bombs were hit during one laser volley to avoid BIG problems */
  38. int bomb_index;               /* Index to hit bombs */
  39. int redraw[MAXREDRAW][3];     /* Coordinates of pieces for redraw() */
  40. ULONG loss[GRIDX * GRIDY];    /* Keeps track of which pieces are destroyed */
  41. int loss_index;               /* Index to loss array */
  42. char game_name[GAMENAMELENGTH]; /* Path/file of current game */
  43. char default_game[GAMENAMELENGTH]; /* Current game name */
  44. struct TextAttr font =       /* Use the 80-column ROM topaz font */
  45. {
  46.   (STRPTR)"topaz.font",
  47.   TOPAZ_EIGHTY,
  48.   FS_NORMAL,
  49.   FPF_ROMFONT
  50. };
  51.  
  52. struct game_format
  53. {
  54.   int turn;
  55.   int moves_left;
  56.   int moves_taken;
  57.   BOOL hyper_up;
  58.   int selected_x;
  59.   int selected_y;
  60.   BOOL select_flag;
  61.   int selected_f;
  62.   ULONG end_game;
  63.   ULONG grid[GRIDX][GRIDY];
  64.   int gate[4];
  65.   ULONG loss[GRIDX * GRIDY];
  66.   int loss_index;
  67. };
  68.  
  69. /* Boolean variables */
  70. int turn;                    /* RED or GREEN */
  71. BOOL hyper_up;               /* TRUE if hypersquare is currently UP */
  72. ULONG end_game;
  73. BOOL select_flag;            /* TRUE if a piece is currently selected */
  74. BOOL master_loop;            /* FALSE when game is to end completely */
  75. BOOL main_loop;              /* FALSE when current game is to restart */
  76.  
  77.  
  78.  
  79. main()
  80. {
  81.   one_time_init(); /* Initialize game (ADVDOOR.C) */
  82.   loop();          /* Main loop of game */
  83.   cleanup(NOERRORS);
  84. }
  85.  
  86.  
  87.  
  88. loop()
  89. {
  90.   master_loop = main_loop = TRUE;
  91.  
  92.   background_sound(TRUE, 0);  /* Start background sound at zero volume */
  93.   while(master_loop)
  94.   {
  95.     title(); /* Animate Advanced Laser Chess title */
  96.     load_game_data(QUIET);  /* Load initial game off disk */
  97.     init_game(); /* Initialize the game */
  98.     fade_all(FALSE, TRUE);  /* Fade colors from background & modify background sound */
  99.     fade();
  100.     while(main_loop)
  101.     {
  102.       /* If done with moves, start other player turn. */
  103.       if(moves_left <= 0) start_player_turn();
  104.       handle_user(); /* Take care of user input */
  105.     }
  106.     fade_all(TRUE, TRUE);   /* Fade all colors to background */
  107.     main_loop = TRUE; /* Reset this in case of a restart */
  108.   }
  109. }
  110.  
  111.  
  112.  
  113. start_player_turn() /* Handles the start of every player turn */
  114. {
  115.   turn = turn == RED? GREEN : RED;
  116.   moves_left = 3;
  117.   moves_taken = 0;
  118.  
  119.   if(select_flag)
  120.   {
  121.     select_flag = FALSE;
  122.     draw_square(selected_x, selected_y);
  123.     draw_piece(selected_x, selected_y, NORMAL);
  124.   }
  125.   work_gates();
  126.   fade();
  127.   draw_moves(moves_left);
  128.   return_to_normal();
  129. }
  130.  
  131.  
  132.  
  133. /* Returns stunned pieces to their normal state and clears flags on hypergons, crunchagons, and lasers. */
  134. return_to_normal()
  135. {
  136.   int x, y;
  137.  
  138.   if(end_game != 0L) return; /* If the game has ended, don't return any pieces */
  139.   for(x=0; x<GRIDX; x++)
  140.     for(y=0; y<GRIDY; y++)
  141.     {
  142.       /* Do not affect empty squares, grid gates or displacement mechanism!! */
  143.       if(grid[x][y] != OTHER && grid[x][y] != 0L)
  144.       {
  145.         /* If this piece is a king, laser, hypergon, or crunchagon... */
  146.         if(grid[x][y] & (KING | KILLLASER | STUNLASER | HYPERGON | FULLOCTAGON | PARTOCTAGON))
  147.           grid[x][y] &= ~FIRED;   /* Mask out FIRED flag (same as HYPERED and CRUNCHED flags). */
  148.         if(!(grid[x][y] & ALIVE))  /* If piece is stunned... */
  149.           if((rnd(100)) < STUNRETURN)  /* STUNRETURN percent chance of being returned to normal state */
  150.           {
  151.             grid[x][y] |= ALIVE;   /* Return ALIVE bit in ID */
  152.             draw_piece(x, y, NORMAL); /* Draw in normal condition */
  153.           }
  154.       }
  155.     }
  156. }
  157.  
  158.  
  159.  
  160. work_gates()   /* Handles the grid gates and hyper square */
  161. {
  162.   int hs, ggs[4], i;
  163.  
  164.   /* Change status of all gates if random < chance of change */
  165.   for(i=0; i<4; i++)
  166.   {
  167.     ggs[i] = gate[i];    /* Save status of gate before possible change */
  168.     gate[i] = (rnd(100) < GGATESTATUS)? OPEN : CLOSED;
  169.     if(gate[i] != ggs[i])     /* If changed status... */
  170.     {
  171.       if(gate[i] == OPEN) up_square(gic[i]); /* Animate */
  172.       else down_square(gic[i]);
  173.     }
  174.   }
  175.   hs = hyper_up;              /* Save hyper square status */
  176.   hyper_up = (rnd(100) < HYPERSTATUS)? TRUE : FALSE;
  177.   if(hs != hyper_up)          /* If changed status... */
  178.   {
  179.     if(hyper_up) up_hyper();  /* Animate accordingly */
  180.     else down_hyper();
  181.   }
  182. }
  183.  
  184.  
  185.  
  186. handle_user()  /* Waits on the backdrop window's IDCMP for messages */
  187. {
  188.   int class, code, qualifier, x, y;
  189.  
  190.   monitor_window_idcmp(&class, &code, &qualifier, &x, &y);
  191.   if(class == MOUSEBUTTONS && (code == SELECTDOWN || code == MENUDOWN))
  192.   {
  193.     if(x >= GRIDLEFT && x <= GRIDRIGHT && y >= GRIDTOP && y <= GRIDBOTTOM)
  194.       check_board(x, y, code);
  195.     else check_icons(x, y);
  196.   }
  197.   else if(class == RAWKEY && (code == 0xC0)) /* Detect space bar */
  198.     background_sound(TOGGLE, BACKGROUND_VOLUME);
  199. }
  200.  
  201.  
  202.  
  203. release_mouse_buttons()
  204. {
  205.   while(!release());
  206. }
  207.  
  208.  
  209.  
  210. release()  /* Returns TRUE if a mouse-up message is received, else FALSE */
  211. {
  212.   register int class, code;
  213.   register struct IntuiMessage *msg;
  214.  
  215.   msg = (struct IntuiMessage *)GetMsg(window->UserPort);
  216.   if(msg == NULL)
  217.     return(FALSE);
  218.   class = msg->Class;
  219.   code = msg->Code;
  220.   ReplyMsg(msg);
  221.  
  222.   if(class == MOUSEBUTTONS && (code == SELECTUP || code == MENUUP))
  223.     return(TRUE);
  224.   else return(FALSE);
  225. }
  226.  
  227.  
  228.  
  229. check_icons(mx, my)
  230. register int mx, my;
  231. {
  232.   /* I admit.  I could have used Intuition for the icons (gadgets).  There
  233.   are several reasons why I didn't: to have Intuition render a gadget's
  234.   image, it requires the image memory to be in consecutive words; that
  235.   is, the image can not exist within several bitplanes as it does within
  236.   bitmap/screen memory.  Therefore I would have to allocate additional
  237.   CHIP memory and copy the icon images from the image screen where they
  238.   are defined to the seperate CHIP RAM buffer to lay out the memory
  239.   consecutively.  I didn't think it was worth the extra memory and
  240.   trouble (I'd rather do things the old-fashioned way and independantly
  241.   blit the icons on to the screen in this case).  True, I could have defined
  242.   gadets with no imagery (blitting the images myself) so I could recieve
  243.   gadget messages through the backdrop window's IDCMP, but again I didn't
  244.   feel like bothering [with] Intuition.  All of the user's mouse clicks
  245.   come through the IDCMP anyway, so I might as well check for icon
  246.   selection the old-fashioned way.  Nothing against Intuition... */
  247.  
  248.   if(mx > DISKX1 && mx < DISKX2 && my > DISKY1 && my < DISKY2) /* Disk icon */
  249.     save_or_restore();
  250.  
  251.   else if(mx > QUITX1 && mx < QUITX2 && my > QUITY1 && my < QUITY2) /* Quit icon */
  252.     main_loop = master_loop = FALSE;
  253.  
  254.   else if(mx > RESTARTX1 && mx < RESTARTX2 && my > RESTARTY1 && my < RESTARTY2)
  255.     main_loop = FALSE;
  256.  
  257.   else if(mx > FIREX1 && mx < FIREX2 && my > FIREY1 && my < FIREY2)
  258.     fire_laser();   /* Fire that laser! */
  259.  
  260.   else if(mx > PASSX1 && mx < PASSX2 && my > PASSY1 && my < PASSY2)
  261.   {
  262.     if(end_game != 0L) return;     /* If the game has ended, don't let this happen. */
  263.     moves_left = 0;                /* Otherwise, cause the turn to be over */
  264.   }
  265.  
  266.   else if(mx > LOSSX1 && mx < LOSSX2 && my > LOSSY1 && my < LOSSY2)
  267.     show_losses();
  268.  
  269.   else if(mx > POSSIBLE_MOVESX1 && mx < POSSIBLE_MOVESX2 &&
  270.   my > POSSIBLE_MOVESY1 && my < POSSIBLE_MOVESY2)
  271.     display_possible();
  272. }
  273.  
  274.  
  275.  
  276. /* Dispatches movement and rotations. */
  277. check_board(x, y, mouse_code)
  278. int x, y, mouse_code;
  279. {
  280.   int gx, gy;  /* Grid coordinates of mouse position */
  281.  
  282.   mouse_to_grid(x, y, &gx, &gy);   /* Get grid coordinates */
  283.  
  284.   /* If hit right button, try to rotate selected piece (if any). */
  285.   if(mouse_code == MENUDOWN)
  286.   {
  287.     if(select_flag) /* If piece selected, try to rotate it. */
  288.       rotate_piece();
  289.     else  /* Otherwise, squawk. */
  290.       beep();
  291.   }
  292.  
  293.   else if(mouse_code == SELECTDOWN) /* If hit left button... */
  294.     if(select_flag)  /* If a piece is already selected, attempt to move. */
  295.       move_piece(gx, gy);
  296.     else
  297.       /* If player is trying to select other's piece (a no-no)... */
  298.       /* or the piece is not currently functional, let him/her know about it. */
  299.       if(!(grid[gx][gy] & turn) || !(grid[gx][gy] & ALIVE) || grid[gx][gy] == OTHER)
  300.         beep();
  301.       else  /* Otherwise, select the piece */
  302.         select_piece(gx, gy);
  303. }
  304.  
  305.  
  306.  
  307. select_piece(gx, gy)  /* Selects a piece; pass grid coordinates */
  308. register int gx, gy;
  309. {
  310.   selected_x = gx;
  311.   selected_y = gy;
  312.   select_flag = TRUE;
  313.   selected_f = facing(grid[gx][gy]); /* Save original facing */
  314.   moves_taken = 0;
  315.   highlight_square(gx, gy, NORMAL);
  316. }
  317.  
  318.  
  319.  
  320. show_losses()
  321. {
  322.   static struct IntuiText itext =
  323.   {
  324.     0, 1,    /* FrontPen, BackPen */
  325.     JAM1,    /* Drawing mode */
  326.     0, 0,    /* LeftEdge, TopEdge */
  327.     &font,   /* ITextFont */
  328.     (UBYTE *)NULL, /* Text (filled in below) */
  329.     NULL     /* NextText */
  330.   };
  331.   ULONG save_id[2][MAXPIECES];
  332.   register int x, y, s, i, sum;
  333.   char string[4];
  334.  
  335.   copy_screen(current_bitmap, &backup_bitmap);  /* Save current screen */
  336.   clear_screen(current_bitmap);
  337.  
  338.   set_color(FADE_COLOR, COLOR_LOSS_BOX);  /* Draw the save/restore box */
  339.   draw_box(LOSS_BOX_WIDTH, LOSS_BOX_HEIGHT, FADE_COLOR);
  340.  
  341.   itext.FrontPen = WHITE;
  342.   itext.IText = (UBYTE *)"LOSSES";
  343.   PrintIText(&screen->RastPort, &itext, 135L, 10L);
  344.  
  345.   itext.FrontPen = LIGHT_BLUE;
  346.   for(x=0; x<2; x++)  /* Save IDs currently on grid */
  347.     for(y = s = 0; y<MAXPIECES; y++)
  348.     {
  349.       save_id[x][y] = grid[x * (GRIDX - 2)][y];
  350.       if((KILLLASER << s) & KING) s++;  /* Skip the king image */
  351.       grid[x * (GRIDX - 2)][y] = (RED << x) | (KILLLASER << s) | ALIVE;
  352.       draw_piece(x * (GRIDX - 2), y, NORMAL);
  353.       s++;
  354.  
  355.       for(i = sum = 0; i<loss_index; i++)
  356.         if((loss[i] & ~(ALIVE | FIRED) & 0x1FFFFFFF) ==
  357.         (grid[x * (GRIDX - 2)][y] & ~(ALIVE | FIRED) & 0x1FFFFFFF))
  358.           ++sum;
  359.  
  360.       sprintf(string, "x%d", sum);
  361.       itext.IText = (UBYTE *)string;
  362.       PrintIText(&screen->RastPort, &itext, (long)(GRIDLEFT +
  363.        GRIDSQUAREWIDTH + GRIDSQUAREWIDTH * x * (GRIDX - 2)),
  364.        (long)(GRIDTOP + 4 + GRIDSQUAREHEIGHT * y));
  365.     }
  366.  
  367.   release_mouse_buttons();
  368.   copy_screen(&backup_bitmap, current_bitmap);
  369.   for(x=0; x<2; x++)  /* Restore IDs */
  370.     for(y=0; y<MAXPIECES; y++)
  371.       grid[x * (GRIDX - 2)][y] = save_id[x][y];
  372. }
  373.  
  374.  
  375.  
  376. /* Displays possible moves of the currently selected piece according to
  377.    the type of piece and the number of remaining moves.  */
  378. display_possible()
  379. {
  380.   int xr, yr;
  381.   ULONG id;
  382.  
  383.   if(select_flag == FALSE)
  384.     return;
  385.  
  386.   copy_screen(current_bitmap, &backup_bitmap);
  387.   for(xr = selected_x - 3; xr < selected_x + 4; xr++)
  388.     for(yr = selected_y - 3; yr < selected_y + 4; yr++)
  389.       if(xr >= 0 && yr >= 0 && xr < GRIDX && yr < GRIDY)
  390.         if(within_range(xr, yr))
  391.           if(grid[xr][yr] != OTHER)
  392.           {
  393.             if(grid[xr][yr] != NULL)
  394.             {
  395.               /* Mask off rotation bits */
  396.               id = grid[selected_x][selected_y] & 0x1FFFFFFF;
  397.               /* If the piece is a crunchagon or king or hypergon... */
  398.               if(id & (FULLOCTAGON | PARTOCTAGON | KING | HYPERGON))
  399.                 if(!(id & CRUNCHED)) /* If hasn't crunched/hypered yet... */
  400.                   highlight_square(xr, yr, POSSIBLE);
  401.             }
  402.             else
  403.               highlight_square(xr, yr, POSSIBLE);
  404.           }
  405.   release_mouse_buttons();
  406.   copy_screen(&backup_bitmap, current_bitmap);
  407. }
  408.  
  409.  
  410.  
  411. fire_laser()
  412. {
  413.   register int i, rgb;
  414.   unsigned long save;
  415.  
  416.   if(!fire_ok()) return;
  417.   laser_sound(turn);
  418.   moves_left -= (moves_taken + 1); /* Subtract number of moves taken */
  419.   moves_taken = 0;
  420.   draw_moves(moves_left);
  421.   /* Save this screen for later restoral */
  422.   copy_screen(current_bitmap, &backup_bitmap);
  423.   rgb = GetRGB4(screen->ViewPort.ColorMap, DARK_SQUARE); /* Get color of dark square */
  424.   set_color(LASER_DARK, rgb);
  425.   rgb = GetRGB4(screen->ViewPort.ColorMap, LIGHT_SQUARE); /* Get color of light square */
  426.   set_color(LASER_LIGHT, rgb);
  427.  
  428.   for(i=0; i<MAXREDRAW; i++)
  429.     redraw[i][0] = -1;        /* Initialize redraw array */
  430.   for(i=0; i<4; i++)
  431.     bomb_hit_list[i][0] = -1; /* Reset bomb hit list */
  432.   cas_index = 0;              /* Reset bomb casualty index */
  433.   bomb_index = 0;             /* Reset bomb index */
  434.  
  435.   move_laser();               /* Do the actual laser movement */
  436.   draw_border();              /* Redraw border because laser interferes with it */
  437.   redraw_pieces();            /* Redraw all the pieces that were hit with laser */
  438.   flicker_beam();             /* Make the beam appear to flicker */
  439.   laser_sound_off();          /* Turn off laser sound */
  440.   /* Restore previously saved screen (to erase laser) */
  441.   copy_screen(&backup_bitmap, current_bitmap);
  442.  
  443.   for(i=0; i<cas_index; i++)  /* Erase any casualties hit by bomb */
  444.   {
  445.     if(grid[ cas_list[i][0] ][ cas_list[i][1] ] & KING) /* If a king was one of the victims... */
  446.       end_game |= grid[ cas_list[i][0] ][ cas_list[i][1] ];
  447.     loss[loss_index++] = grid[ cas_list[i][0] ][ cas_list[i][1] ];
  448.     grid[ cas_list[i][0] ][ cas_list[i][1] ] = 0L;      /* Erase victim from grid memory */
  449.     draw_square(cas_list[i][0], cas_list[i][1]);    /* Erase the piece from screen */
  450.   }
  451.  
  452.   explode();                  /* Do any explosions caused by laser fire */
  453.   select_flag = FALSE;
  454.   draw_square(selected_x, selected_y);
  455.   save = grid[selected_x][selected_y];   /* Save ID of firer */
  456.   grid[selected_x][selected_y] |= ALIVE; /* Make piece appear to be alive, even if it wasn't, so a shadow will be drawn for the stunned piece */
  457.   draw_piece(selected_x, selected_y, NORMAL); /* Draw the piece normally (with shadow) */
  458.   grid[selected_x][selected_y] = save;        /* Restore ID and draw again, in case the piece was stunned */
  459.   draw_piece(selected_x, selected_y, NORMAL);
  460.   if(end_game) game_end(end_game);
  461. }
  462.  
  463.  
  464.  
  465. move_laser()   /* Actually moves the laser */
  466. {
  467.   int vn, i, j, prev_hit, prev_redrawn, lxd, lyd, x, y, reflect, split_flag, first;
  468.   int stun, rindex, lindex, rci;
  469.   unsigned long hit;
  470.  
  471.   rindex = -1;  /* Redraw index */
  472.   lindex = -1;  /* Laser index - how many squares moved.  If greater than MAXLSRTRAV, the laser is aborted. */
  473.   if(grid[selected_x][selected_y] & STUNLASER)
  474.     stun = TRUE;
  475.   else stun = FALSE;
  476.   first = TRUE;                         /* First segment drawn- used so laser can be drawn over beam */
  477.   split_flag = FALSE;                   /* Indicates no split from beam-splitter */
  478.   split_index = split_max = vn = 0;     /* No splits or victims as of yet */
  479.   for(i=0; i<8; i++)                    /* Clear out the identical split array */
  480.     ident_split[i][0] = ident_split[i][1] = -1;   /* -1 is a coordinate that can't be achieved on the grid. */
  481.   x = selected_x;  y = selected_y;      /* Laser origin */
  482.   laser_direction(&lxd, &lyd);          /* Get original laser direction */
  483.  
  484.   while(TRUE)                           /* Infinite loop - routine exits by a return. */
  485.   {
  486.     draw_beam_segment(x, y, lxd, lyd);
  487.     if(first)
  488.     {
  489.       draw_piece(x, y, NORMAL | NOSHADOW);
  490.       first = FALSE;
  491.     }
  492.     x += lxd;  y += lyd;      /* Update laser grid coordinates */
  493.  
  494.     if(++lindex > MAXLSRTRAV || x < 0 || x > 14 || y < 0 || y > 10)   /* If beam is stuck in loop or went out of grid boundary... */
  495.     {
  496.       if(next_split(&x, &y, &lxd, &lyd, &lindex))
  497.         return;     /* If through with all splits, return. */
  498.     }
  499.     else            /* If laser is still within grid boundary... */
  500.       if(hit = beam_hit(x, y, &lxd, &lyd, &reflect, &split_flag)) /* If hit something... */
  501.       {
  502.         prev_redrawn = FALSE;
  503.         for(rci=0; rci<rindex; rci++)
  504.           if(redraw[rci][0] == x && redraw[rci][1] == y)
  505.             {prev_redrawn = TRUE; break;}
  506.         if(!prev_redrawn)
  507.         {
  508.           if(rindex < MAXREDRAW) ++rindex;
  509.           redraw[rindex][0] = x;
  510.           redraw[rindex][1] = y;
  511.           /* Default value - could be changed below to HIGHLIGHT */
  512.           redraw[rindex][2] = NORMAL | NOSHADOW;
  513.         }
  514.         if(split_flag) /* If hit a splitter... */
  515.         {
  516.           if(split_beam(x, y, lxd, lyd)) /* If split didn't occur before... */
  517.             if(next_split(&x, &y, &lxd, &lyd, &lindex))   /* Continue on previous split (if applicable) */
  518.               return;
  519.         }
  520.         else
  521.         {
  522.           if(!reflect && hit != OTHER)     /* If piece did not reflect and it isn't a grid square or hypersquare... */
  523.           {
  524.             /* Draw piece as highlighted but without shadow */
  525.             draw_piece(x, y, HIGHLIGHT | NOSHADOW);
  526.             if(stun)     /* If firing a stun laser... */
  527.             {
  528.               /* Also draw piece on background screen so when it is
  529.                  refreshed, the pieces still appear stunned */
  530.               draw_back(x, y, HIGHLIGHT | NOSHADOW);
  531.               if(prev_redrawn) redraw[rci][2] = HIGHLIGHT | NOSHADOW;
  532.               else redraw[rindex][2] = HIGHLIGHT | NOSHADOW;
  533.             }
  534.             else
  535.             {
  536.               prev_hit = FALSE;              /* Previously hit flag */
  537.               for(j=0; j<vn; j++)            /* Check for previous identical victim, just in case on piece gets hit more than once */
  538.                 if(explode_info[j].flag)
  539.                   if(explode_info[j].x == x && explode_info[j].y == y)
  540.                     prev_hit = TRUE;
  541.               if(!prev_hit)   /* Do the below only if the piece wasn't previously hit. */
  542.               {
  543.                 explode_info[vn].flag = TRUE;
  544.                 explode_info[vn].x = x;
  545.                 explode_info[vn].y = y;
  546.                 ++vn; /* Increment number of victims */
  547.                 if(hit & KING) end_game |= hit; /* If hit a king, end of game! */
  548.                 if(prev_redrawn) redraw[rci][2] = HIGHLIGHT | NOSHADOW;
  549.                 else redraw[rindex][2] = HIGHLIGHT | NOSHADOW;
  550.               }
  551.             }
  552.             if(next_split(&x, &y, &lxd, &lyd, &lindex)) return; /* Continue on previous split (if applicable) */
  553.           }
  554.         }
  555.       }
  556.   }
  557. }
  558.  
  559.  
  560.  
  561. /* Returns ID if there is a piece at (x, y). */
  562. /* If it reflects the beam, a new lxd and lyd are returned and reflect is
  563.    set to TRUE.  If a beam splitter splits a beam, split_flag is set to TRUE. */
  564. unsigned long beam_hit(x, y, lxd, lyd, reflect, split_flag)
  565. int x, y, *lxd, *lyd, *reflect, *split_flag;
  566. {
  567.   unsigned long id, f;
  568.   int pass;  /* Flag used for beam-splitter (TRUE if laser goes through splitter square without harming it */
  569.   int rand_dir;
  570.  
  571.   *split_flag = FALSE;             /* FALSE by default */
  572.   if(grid[x][y] == 0L) return(FALSE); /* If nothing there, return a FALSE. */
  573.   if(grid[x][y] == OTHER)          /* If hit hypersquare or a closed grid gate... */
  574.   {
  575.     if(x == GRIDX / 2 && y == GRIDY / 2) /* If hit hypersquare... */
  576.     {
  577.       if(hyper_up)                 /* If the hypersquare is active... */
  578.       {
  579.         rand_dir = rnd(8);
  580.         *lxd = xreflect[rand_dir];      /* Randomize laser direction! */
  581.         *lyd = yreflect[rand_dir];
  582.         *reflect = TRUE;
  583.         return(grid[x][y]);
  584.       }
  585.       else *reflect = FALSE; return(FALSE);
  586.     }
  587.     else *reflect = FALSE; return(FALSE);
  588.   }
  589.   id = grid[x][y] & 0x1FFFFFFE;    /* Mask off rotation and ALIVE bits */
  590.   f = facing(grid[x][y]);          /* Get facing of piece */
  591.   if(!(grid[x][y] & ALIVE))        /* If the piece has been stunned (vulnerable!)... */
  592.     if(!(grid[x][y] & (TRIMIRROR | SPLITTER))) /* These are special cases (beams may pass through their squares under certain conditions */
  593.       {*reflect = FALSE; return(id);} /* Indicate that piece doesn't reflect but has been hit. */
  594.   if(id & (KILLLASER | KING))      /* These pieces have NO reflective surfaces. */
  595.     {*reflect = FALSE; return(id);} /* Indicate that the piece doesn't reflect, but has been hit. */
  596.   if(id & ONEWAYMIRROR)            /* If hit a one-way mirror... */
  597.     switch(one_way_mirror(lxd, lyd, f))
  598.     {
  599.       case 0: *reflect = TRUE;  return(id); break;    /* If reflected, return ID */
  600.       case 1: *reflect = TRUE;  return(id); break;    /* If went through one-way */
  601.       case 2: *reflect = FALSE; return(id); break;    /* Piece is hit */
  602.     }
  603.   if(id & STUNLASER)               /* If hit a stun laser */
  604.     {*reflect = stun_laser(lxd, lyd, f); return(id);}
  605.   if(id & PARTOCTAGON)             /* If hit a partially-mirrored octagon... */
  606.     {*reflect = part_octagon(lxd, lyd, f); return(id);}
  607.   if(id & TRIMIRROR)               /* If hit a triangular mirror... */
  608.     switch(tri_mirror(lxd, lyd, f))
  609.     {
  610.       case 0: *reflect = FALSE; return(id); break;     /* Piece is hit (not reflected) */
  611.       case 1:  /* If the piece reflects the beam... */
  612.         if(!(grid[x][y] & ALIVE)) *reflect = FALSE;  /* If stunned, don't reflect */
  613.         else *reflect = TRUE; /* Otherwise, reflect. */
  614.         return(id);
  615.         break;
  616.       case 2: *reflect = FALSE; return(FALSE); break;  /* If beam passed through square, act like nothing is there. */
  617.     }
  618.   if(id & FULLOCTAGON)             /* If hit a fully-mirrored octagon... */
  619.   {
  620.     *reflect = TRUE;
  621.     *lxd = -*lxd;  *lyd = -*lyd;   /* Reverse beam direction */
  622.     return(id);
  623.   }
  624.   if(id & HYPERGON)                /* If hit a hypergon... */
  625.     {*reflect = TRUE; hypergon(lxd, lyd); return(id);}
  626.   if(id & BOMB)                    /* If hit a bomb... */
  627.     {hit_bomb(x, y, *lxd, *lyd); *reflect = FALSE; return(id);}
  628.   if(id & SPLITTER)                /* If hit a beam-splitter... */
  629.   {
  630.     *reflect = splitter(x, y, lxd, lyd, f, split_flag, &pass);
  631.     if(pass) return(FALSE);   /* If laser passed through square, act like nothing is there */
  632.     else return(id);          /* Otherwise, return splitter ID */
  633.   }
  634. }
  635.  
  636.  
  637. split_beam(x, y, lxd, lyd)         /* Initiates a split from splitter at (x, y) */
  638. int x, y, lxd, lyd;
  639. {
  640.   int i;
  641.  
  642.   for(i=0; i<split_max; i++)       /* Check for identical splits */
  643.     if(ident_split[i][0] == x && ident_split[i][1] == y)
  644.       return(TRUE); /* Indicate identical split */
  645.  
  646.   split[split_index][0] = x;       /* Save grid coordinates of split */
  647.   split[split_index][1] = y;
  648.   split[split_index][2] = -lxd;    /* Save opposite of current beam direction */
  649.   split[split_index][3] = -lyd;    /* (where the laser will continue from */
  650.   ident_split[split_max][0] = x;   /* Acknowledge these splitter coordinates as the position of a new splitter */
  651.   ident_split[split_max][1] = y;
  652.   ++split_index;                   /* Increment number of splits */
  653.   ++split_max;                     /* Increment maximum number of splits that occured */
  654.   return(FALSE);                   /* Indicate successful split */
  655. }
  656.  
  657.  
  658.  
  659. next_split(x, y, lxd, lyd, lindex) /* Resumes laser at previous split if applicable, returns TRUE otherwise. */
  660. int *x, *y, *lxd, *lyd, *lindex;   /* Also resets lindex if splits are not through. */
  661. {
  662.   if(split_index == 0) return(TRUE);    /* Indicate no more splits */
  663.   --split_index;
  664.   *x = split[split_index][0];      /* Resume split coordinates */
  665.   *y = split[split_index][1];
  666.   *lxd = split[split_index][2];    /* and direction */
  667.   *lyd = split[split_index][3];
  668.   *lindex = 0;                     /* Reset laser travelling index */
  669.   return(FALSE);                   /* Indicate more to go */
  670. }
  671.  
  672.  
  673.  
  674. one_way_mirror(lxd, lyd, f)      /* Checks for beam reflection on a one-way mirror; returns TRUE if reflects */
  675. int *lxd, *lyd;
  676. unsigned long f;
  677. {
  678.   if(xreflect[f] == *lxd && yreflect[f] == *lyd)     /* If beam reflects... */
  679.   {
  680.     *lxd = -*lxd;  *lyd = -*lyd;   /* Reverse beam direction */
  681.     return(0);                     /* Indicate that piece reflected */
  682.   }
  683.   f += 4;  f &= 7;
  684.   if(xreflect[f] == *lxd && yreflect[f] == *lyd)
  685.     return(1);                     /* Indicate that laser went through one-way */
  686.   return(2);                       /* Otherwise, piece didn't reflect; it is hit. */
  687. }
  688.  
  689.  
  690.  
  691. stun_laser(lxd, lyd, f)  /* Checks stun laser for beam reflection */
  692. int *lxd, *lyd;
  693. unsigned long f;
  694. {
  695.   int index, reflect;
  696.  
  697.   reflect = FALSE;
  698.   index = f;
  699.   if(xreflect[f] == *lxd && yreflect[f] == *lyd) reflect = TRUE;
  700.   index += 2;  index &= 7;  /* Make sure index is 0 - 7. */
  701.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) reflect = TRUE;
  702.   index += 4;  index &= 7;
  703.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) reflect = TRUE;
  704.   if(reflect)
  705.   {
  706.     *lxd = -*lxd;   *lyd = -*lyd;  /* Reverse beam direction */
  707.     return(TRUE);   /* Indicate reflection */
  708.   }
  709.   return(FALSE);    /* Indicate no reflection */
  710. }
  711.  
  712.  
  713.  
  714. part_octagon(lxd, lyd, f)     /* Checks for reflection on partially-mirrored octagons */
  715. int *lxd, *lyd;
  716. unsigned long f;
  717. {
  718.   int index, reflect;
  719.  
  720.   reflect = FALSE;
  721.   index = f;
  722.   if(xreflect[f] == *lxd && yreflect[f] == *lyd) reflect = TRUE;
  723.   ++index;  index &= 7;
  724.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) reflect = TRUE;
  725.   index += 6;  index &= 7;
  726.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) reflect = TRUE;
  727.   if(reflect)
  728.   {
  729.     *lxd = -*lxd;  *lyd = -*lyd;   /* Reverse beam direction */
  730.     return(TRUE);   /* Indicate reflection */
  731.   }
  732.   return(FALSE);    /* Indicate no reflection */
  733. }
  734.  
  735.  
  736.  
  737. tri_mirror(lxd, lyd, f)  /* Checks for reflection on triangular mirrors */
  738. int *lxd, *lyd;          /* Return values: 0=no reflect, 1=reflect, 2=pass through */
  739. unsigned long f;
  740. {
  741.   int index;
  742.  
  743.   index = f;
  744.   if(xreflect[f] == *lxd && yreflect[f] == *lyd)  /* If hit head-on... */
  745.   {
  746.     *lxd = -*lxd;  *lyd = -*lyd;   /* Reverse beam direction */
  747.     return(1); /* Indicate reflection */
  748.   }
  749.   ++index;  index &= 7;
  750.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If hit at angle... */
  751.   {
  752.     index += 2;  index &= 7;
  753.     *lxd = xreflect[index];
  754.     *lyd = yreflect[index];
  755.     return(1);
  756.   }
  757.   index += 6;  index &= 7;
  758.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If hit at other angle... */
  759.   {
  760.     index -= 2;  index &= 7;
  761.     *lxd = xreflect[index];
  762.     *lyd = yreflect[index];
  763.     return(1);
  764.   }
  765.   index = f + 2;  index &= 7;
  766.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If beam is going parallel to reflective surface... */
  767.     return(2); /* Indicate a beam pass-through */
  768.   index += 4;  index &= 7;
  769.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If beam is going parallel in other direction... */
  770.     return(2); /* Indicate a beam pass-through */
  771.   return(0);   /* Indicate no reflection; the piece is hit */
  772. }
  773.  
  774.  
  775.  
  776. hypergon(lxd, lyd)     /* Deflects laser randomly */
  777. int *lxd, *lyd;
  778. {
  779.   int rand_dir;
  780.  
  781.   rand_dir = rnd(8);
  782.   *lxd = xreflect[rand_dir];      /* Randomize laser direction! */
  783.   *lyd = yreflect[rand_dir];
  784. }
  785.  
  786.  
  787.  
  788. hit_bomb(x, y, lxd, lyd)      /* Takes care of a very special case, the bomb. */
  789. int x, y, lxd, lyd;
  790. {
  791.   static int x_loop[] = {0, 1, 1, 1, 0, -1, -1, -1};
  792.   static int y_loop[] = {-1, -1, 0, 1, 1, 1, 0, -1};
  793.   int i, ax, ay;
  794.  
  795.   for(i=0; i<4; i++)          /* Make sure this bomb wasn't previously hit */
  796.     if(bomb_hit_list[i][0] == x && bomb_hit_list[i][1] == y) return;
  797.   if(facing(grid[x][y]) == 0) /* If the facing does not permit diagonal entry... */
  798.   {
  799.     if(lxd != 0 && lyd != 0)  /* If laser is travelling diagonally... */
  800.       return;                 /* just return- the laser has hit the outside */
  801.   }
  802.   else                        /* If allowing for diagonal laser entry... */
  803.   {
  804.     if(lxd == 0 || lyd == 0)  /* If laser is NOT travelling diagonally... */
  805.       return;                 /* return as before- laser hit outside of piece */
  806.   }
  807.   bomb_hit_list[bomb_index][0] = x;
  808.   bomb_hit_list[bomb_index++][1] = y;
  809.  
  810.   for(i=0; i<8; i++)
  811.   {
  812.     ax = x + x_loop[i];
  813.     ay = y + y_loop[i];
  814.     if(ax > -1 && ax < 15 && ay > -1 && ay < 11)      /* Ignore anything past grid boundaries */
  815.       if(grid[ax][ay] != OTHER && grid[ax][ay] != 0L) /* If there is a PIECE at destination square... */
  816.       {
  817.         draw_beam_segment(x, y, x_loop[i], y_loop[i]);
  818.         draw_piece(ax, ay, HIGHLIGHT | NOSHADOW); /* Draw as stunned */
  819.         grid[ax][ay] &= ~ALIVE;
  820.         if(grid[selected_x][selected_y] & KILLLASER)  /* Is firing piece laser */
  821.          { 
  822.           cas_list[cas_index][0] = ax;
  823.           cas_list[cas_index++][1] = ay;
  824.          }
  825.         else
  826.          { 
  827.           draw_back(ax, ay, HIGHLIGHT | NOSHADOW);
  828.          }
  829.       }
  830.   }
  831. }
  832.  
  833.  
  834.  
  835. /* Checks for reflection/split on beam-splitter */
  836. splitter(x, y, lxd, lyd, f, split_flag, pass)
  837. int x, y, *lxd, *lyd, *split_flag, *pass;
  838. unsigned long f;
  839. {
  840.   int index;
  841.  
  842.   index = f;
  843.   *pass = *split_flag = FALSE;  /* Defaults */
  844.   if(xreflect[f] == *lxd && yreflect[f] == *lyd)  /* If hit on vertex (split) */
  845.   {
  846.     if(!(grid[x][y] & ALIVE))  /* If splitter is stunned... */
  847.       return(FALSE);  /* Indicate no reflection */
  848.     index -= 2;  index &= 7;
  849.     *lxd = xreflect[index];
  850.     *lyd = yreflect[index];
  851.     *split_flag = TRUE;
  852.     return(TRUE);  /* Indicate reflection */
  853.   }
  854.   index += 2;  index &= 7;
  855.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If hit proportional to vertex... */
  856.   {
  857.     if(!(grid[x][y] & ALIVE))  /* If splitter is stunned... */
  858.       return(FALSE);  /* Indicate no reflection */
  859.     index += 2;  index &= 7;
  860.     *lxd = xreflect[index];
  861.     *lyd = yreflect[index];
  862.     return(TRUE); /* Indicate reflection */
  863.   }
  864.   index += 4;  index &= 7;
  865.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If hit proportional to vertex the other way... */
  866.   {
  867.     if(!(grid[x][y] & ALIVE))  /* If splitter is stunned... */
  868.       return(FALSE);  /* Indicate no reflection */
  869.     index -= 2;  index &= 7;
  870.     *lxd = xreflect[index];
  871.     *lyd = yreflect[index];
  872.     return(TRUE);
  873.   }
  874.   index = f + 4;  index &= 7;
  875.   if(xreflect[index] == *lxd && yreflect[index] == *lyd) /* If hit on base... */
  876.     return(FALSE);
  877.   *pass = TRUE; /* At this point, the laser must have passed through the square without hitting splitter */
  878.   return(FALSE);
  879. }
  880.  
  881.  
  882.  
  883. flicker_beam() /* Makes beam appear to flicker */
  884. {
  885.   register UWORD rgblaser, rgblight, rgbdark;
  886.  
  887.   if(turn == GREEN)
  888.     rgblaser = COLOR_BLUE;
  889.   else
  890.     rgblaser = COLOR_ORANGE;
  891.   rgblight = GetRGB4(screen->ViewPort.ColorMap, LIGHT_SQUARE);
  892.   rgbdark = GetRGB4(screen->ViewPort.ColorMap, DARK_SQUARE);
  893.  
  894.   do
  895.   {
  896.     if(rnd(100) < FLICKER)   /* If beam is to be shown... */
  897.     {
  898.       /* Get color of light square */
  899.       set_color(LASER_LIGHT, rgblaser);
  900.       set_color(LASER_DARK, rgblaser);
  901.     }
  902.     else             /* Otherwise, make the colors match the grid */
  903.     {
  904.       set_color(LASER_LIGHT, rgblight);
  905.       set_color(LASER_DARK, rgbdark);
  906.     }
  907.   }while(!release());
  908. }
  909.  
  910.  
  911.  
  912. laser_direction(x, y)    /* Figures out original laser direction */
  913. int *x, *y;
  914. {
  915.   unsigned long f;
  916.  
  917.   f = facing(grid[selected_x][selected_y]);
  918.   if(grid[selected_x][selected_y] & STUNLASER)
  919.     {f += 4;  f &= 7;}
  920.   f += 4;  f &= 7;
  921.   *x = xreflect[f];
  922.   *y = yreflect[f];
  923. }
  924.  
  925.  
  926.  
  927. redraw_pieces()  /* Redraws all pieces in redraw array and turns pieces to stunned status */
  928. {                /* if redraw[i][2] == HIGHLIGHT. */
  929.   register int i;
  930.  
  931.   for(i=0; i<MAXREDRAW; i++)
  932.   {
  933.     if(redraw[i][0] == -1) return; /* Done with redraw list- return */
  934.     if(redraw[i][2] & HIGHLIGHT)  /* If it is to be stunned... */
  935.       grid[redraw[i][0]][redraw[i][1]] &= 0xFFFFFFFE; /* mask off alive bit if stunned. */
  936.     if(grid[redraw[i][0]][redraw[i][1]] & ALIVE)  /* If the piece is NOT stunned... */
  937.       draw_piece(redraw[i][0], redraw[i][1], redraw[i][2]);
  938.   }
  939. }
  940.  
  941.  
  942.  
  943. /* Checks to see if a laser selected and the right number of moves is available */
  944. fire_ok()
  945. {         /* Returns a FALSE if NOT ok to fire, TRUE otherwise. */
  946.   if(!select_flag ||
  947.    !(grid[selected_x][selected_y] & (KILLLASER | STUNLASER)) ||
  948.    (grid[selected_x][selected_y] & FIRED) ||
  949.    moves_left - moves_taken < 1)
  950.   {
  951.     beep();
  952.     return(FALSE);
  953.   }
  954.   grid[selected_x][selected_y] |= FIRED;
  955.   return(TRUE);
  956. }
  957.  
  958.  
  959.  
  960. /* Moves piece from (selected_x, selected_y) to (dx, dy). */
  961. /* The piece is deselected if coordinate pairs match. */
  962. move_piece(dx, dy)
  963. register int dx, dy;
  964. {
  965.   int i;
  966.  
  967.   /* If coordinates match, deselect piece. */
  968.   if(selected_x == dx && selected_y == dy)
  969.   {
  970.     select_flag = FALSE;
  971.     moves_left -= moves_taken;     /* Subtract moves taken by rotation. */
  972.     moves_taken = 0;               /* Zero-out number of moves taken */
  973.     draw_moves(moves_left);
  974.     draw_square(dx, dy);
  975.     draw_piece(dx, dy, NORMAL);
  976.   }
  977.  
  978.   else
  979.   {
  980.     /* Obstacles present in middle row: hyper square and grid gates. */
  981.     if(dx == GRIDX / 2)
  982.     {
  983.       if(dy == GRIDY / 2) /* If moving onto hyper square, hyper piece. */
  984.         {hyper_by_hypersquare(); return;}
  985.       else
  986.         for(i=0; i<4; i++) /* Check for movement into a grid gate. */
  987.           if(dy == gic[i] && gate[i] == CLOSED)
  988.             {destroy_by_gate(dy); return;}
  989.     }
  990.  
  991.     if(grid[dx][dy])  /* If there is something at destination, */
  992.       grid_check(dx, dy);  /* see if it is munched. */
  993.     else if(within_range(dx, dy))
  994.       move(dx, dy);
  995.     else beep();
  996.   }
  997. }
  998.  
  999.  
  1000. move(dx, dy)     /* Does actual work of moving a piece */
  1001. register int dx, dy;
  1002. {
  1003.   unsigned long save;
  1004.  
  1005.   save = grid[dx][dy];         /* Save whatever the piece is moving on to */
  1006.   draw_square(selected_x, selected_y); /* Erase piece at old location */
  1007.   grid[dx][dy] = grid[selected_x][selected_y];   /* Move piece's ID */
  1008.   draw_piece(dx, dy, NORMAL);  /* Draw piece at new location */
  1009.   grid[selected_x][selected_y] = 0L; /* Erase ID at old location */
  1010.   select_flag = FALSE;         /* Make sure piece is marked as deselected */
  1011.   moves_taken += abs(selected_x - dx) + abs(selected_y - dy); /* Absolute distance moved */
  1012.   moves_left -= moves_taken;   /* Subtract moves taken */
  1013.   moves_taken = 0;             /* Zero out number of moves taken */
  1014.   draw_moves(moves_left);      /* Display new number of moves */
  1015.   if(save != 0L)               /* If moved onto a king, end the game. */
  1016.   {
  1017.     if(save & KING)
  1018.     {
  1019.       end_game = save;
  1020.       game_end(save);
  1021.     }
  1022.     else loss[loss_index++] = save;
  1023.   }
  1024. }
  1025.  
  1026.  
  1027.  
  1028. /* Rotates piece at specified grid position */
  1029. rotate_piece()
  1030. {
  1031.   unsigned long id, f;
  1032.  
  1033.   if(!select_flag)  /* If no piece is currently selected... */
  1034.     {beep(); return;}
  1035.  
  1036.   id = grid[selected_x][selected_y] & 0x1FFFFFFF; /* Mask off rotation bits */
  1037.   f = facing(grid[selected_x][selected_y]);
  1038.   if(id & (KING | HYPERGON | FULLOCTAGON))  /* Can't rotate these... */
  1039.   {
  1040.     beep();
  1041.     return;
  1042.   }
  1043.   if(id & (KILLLASER | ONEWAYMIRROR | TRIMIRROR | SPLITTER |
  1044.            STUNLASER | PARTOCTAGON)) /* These can rotate 8 ways */
  1045.   {
  1046.     ++f; f &= 7L;
  1047.   }
  1048.   else if(id & BOMB)
  1049.   {
  1050.     f = !f;
  1051.   }
  1052.  
  1053.   if((int)f == selected_f) /* If rotated to original position, no moves */
  1054.   {                        /* are taken. */
  1055.     moves_taken = 0;
  1056.     draw_moves(moves_left);
  1057.   }
  1058.   else                          /* Otherwise one move is taken. */
  1059.   {
  1060.     moves_taken = 1;
  1061.     draw_moves(moves_left - 1);
  1062.   }
  1063.  
  1064.   grid[selected_x][selected_y] = (ULONG)(id | (f << 29));
  1065.   highlight_square(selected_x, selected_y, NORMAL);
  1066. }
  1067.  
  1068.  
  1069.  
  1070. /* Takes care of piece moving onto something at (dx, dy). */
  1071. grid_check(dx, dy)
  1072. register int dx, dy;
  1073. {
  1074.   unsigned long id;
  1075.  
  1076.   if(!within_range(dx, dy)) {beep(); return;}
  1077.   if(grid[dx][dy] == OTHER) return;  /* Just a precaution */
  1078.   id = grid[selected_x][selected_y] & 0x1FFFFFFF; /* Mask off rotation bits */
  1079.  
  1080.   /* If the piece is a crunching-type octagon or king... */
  1081.   if(id & (FULLOCTAGON | PARTOCTAGON | KING))
  1082.   {
  1083.     /* If the piece has already crunched something this turn... */
  1084.     if(id & CRUNCHED) /* don't let it do it again. */
  1085.       beep();
  1086.     else
  1087.     {
  1088.       /* Indicate that this piece has crunched something this turn. */
  1089.       grid[selected_x][selected_y] |= CRUNCHED;
  1090.       crunch_sound();
  1091.       draw_square(dx, dy); /* Erase what the piece is going to move onto */
  1092.       move(dx, dy); /* Just walk on whatever is at destination. */
  1093.     }
  1094.   }
  1095.  
  1096.   else if(id & HYPERGON) /* If hypergon moves onto a piece... */
  1097.   {
  1098.     if(id & HYPERED)     /* If already hypered something this turn... */
  1099.       beep();
  1100.     else
  1101.       hyper_by_hypergon(dx, dy);  /* Hyper the victim */
  1102.   }
  1103.  
  1104.   else beep();  /* If can't move onto another piece, squawk. */
  1105. }
  1106.  
  1107.  
  1108.  
  1109. /* Destroys a piece that moved into a closed gate or the empty hypersquare */
  1110. destroy_by_gate(dy)
  1111. register int dy;
  1112. {
  1113.   if(within_range(GRIDX / 2, dy))
  1114.   {
  1115.     if(grid[selected_x][selected_y] & KING)  /* If king is destroyed by gate... */
  1116.       end_game = grid[selected_x][selected_y];
  1117.     loss[loss_index++] = grid[selected_x][selected_y];
  1118.     explode_info[0].x = GRIDX / 2;
  1119.     explode_info[0].y = dy;
  1120.     explode_info[0].flag = TRUE;
  1121.     draw_square(selected_x, selected_y);
  1122.     grid[selected_x][selected_y] = 0L;
  1123.     select_flag = FALSE;
  1124.     moves_taken += abs(selected_x - GRIDX / 2) + abs(selected_y - dy);
  1125.     moves_left -= moves_taken;
  1126.     moves_taken = 0;
  1127.     draw_moves(moves_left);
  1128.     explode();
  1129.     if(end_game != 0L)
  1130.       game_end(end_game);
  1131.   }
  1132.   else beep();
  1133. }
  1134.  
  1135.  
  1136.  
  1137. /* Hypers piece due to moving onto the hypersquare. */
  1138. hyper_by_hypersquare()
  1139. {
  1140.   /* Remove piece as if it were moving onto gate if the hypersquare is down */
  1141.   if(!hyper_up)
  1142.     destroy_by_gate(GRIDY / 2);
  1143.   else if(within_range(GRIDX / 2, GRIDY / 2))
  1144.   {
  1145.     select_flag = FALSE;
  1146.     moves_taken += abs(selected_x - GRIDX / 2) + abs(selected_y - GRIDY / 2);
  1147.     moves_left -= moves_taken;
  1148.     moves_taken = 0;
  1149.     draw_moves(moves_left);
  1150.     hyperee_color(selected_x, selected_y);   /* Set the fade color for a hyperee accordingly */
  1151.     draw_square(selected_x, selected_y);     /* De-highlight the hyperee */
  1152.     /* Draw the hyperee in the fade colors for hyper_out(). */
  1153.     draw_piece(selected_x, selected_y, HYPEREE | NOSHADOW);
  1154.     hyper_out(selected_x, selected_y);       /* Draw the hyperee fading out */
  1155.     draw_square(selected_x, selected_y);     /* Erase remnants of hyperee image */
  1156.     hyper_coords(selected_x, selected_y);    /* Hyper piece */
  1157.   }
  1158.   else beep();
  1159. }
  1160.  
  1161.  
  1162.  
  1163. /* Hypers the piece at (x, y) due to getting moved onto by a hypergon. */
  1164. hyper_by_hypergon(dx, dy)
  1165. register int dx, dy;
  1166. {
  1167.   unsigned long save;
  1168.  
  1169.   if(within_range(dx, dy))
  1170.   {
  1171.     select_flag = FALSE;
  1172.     /* Indicate that this piece has hypered something */
  1173.     grid[selected_x][selected_y] |= HYPERED;
  1174.     moves_taken += abs(dx - selected_x) + abs(dy - selected_y);
  1175.     moves_left -= moves_taken;
  1176.     moves_taken = 0;
  1177.     draw_moves(moves_left);
  1178.     hyperee_color(dx, dy);       /* Set hyperee color according to color of piece at (x, y) */
  1179.     draw_square(dx, dy);         /* Erase hyperee */
  1180.     /* Draw the hyperee in the fade colors for hyper_out(). */
  1181.     draw_piece(dx, dy, HYPEREE | NOSHADOW);
  1182.     save = grid[dx][dy];         /* Save the hyperee ID */
  1183.     grid[dx][dy] = grid[selected_x][selected_y];   /* Place hypergon ID at destination for draw_piece(). */
  1184.     draw_square(selected_x, selected_y);           /* Erase hypergon */
  1185.     draw_piece(dx, dy, NORMAL);  /* Draw the hypergon OVER the hyperee */
  1186.     grid[dx][dy] = save;         /* Restore hyperee ID for hyper_out() */
  1187.     hyper_out(dx, dy);           /* Show the hyperee fading out */
  1188.     grid[dx][dy] = grid[selected_x][selected_y];   /* Transfer hypergon in grid memory again */
  1189.     draw_square(dx, dy);         /* Erase over remnants of hyperee */
  1190.     draw_piece(dx, dy, NORMAL);  /* Draw the hypergon at destination again */
  1191.     grid[dx][dy] = save;         /* Restore hyperee ID again */
  1192.     hyper_coords(dx, dy);        /* Hyper the hyperee */
  1193.     grid[dx][dy] = grid[selected_x][selected_y];   /* Yes, transfer hypergon ID one more time */
  1194.     grid[selected_x][selected_y] = 0L; /* Erase old position of hypergon from grid memory */
  1195.   }
  1196.   else beep();
  1197. }
  1198.  
  1199.  
  1200.  
  1201. hyper_coords(x, y)  /* Hypers piece ID at (x, y) */
  1202. register int x, y;
  1203. {
  1204.   register int dx, dy, searching = TRUE;
  1205.   unsigned long id, f;
  1206.  
  1207.   id = grid[x][y];  /* Save hyperee ID for hyper_in() */
  1208.   while(searching)
  1209.   {
  1210.     dx = rnd(GRIDX);  /* Random hyperee destination coordinates */
  1211.     dy = rnd(GRIDY);
  1212.  
  1213.     /* Make sure piece doesn't land in hyper square */
  1214.     if(dx != GRIDX / 2 || dy != GRIDY / 2)
  1215.       if((grid[dx][dy] == 0L || grid[dx][dy] == OTHER))
  1216.         searching = FALSE; /* Exit from loop if found a good "landing spot" */
  1217.   }
  1218.  
  1219.   if(grid[dx][dy] == 0L)           /* If landing in an open spot... */
  1220.   {
  1221.     grid[dx][dy] = grid[x][y];     /* Transfer piece in grid memory */
  1222.     grid[x][y] = 0L;               /* Erase old position from grid memory */
  1223.     id = grid[dx][dy] & 0x1FFFFFFF; /* Mask off rotation bits */
  1224.     f = facing(grid[dx][dy]);
  1225.     if(id & (KILLLASER | ONEWAYMIRROR | TRIMIRROR | SPLITTER |
  1226.          STUNLASER | PARTOCTAGON)) /* These can rotate */
  1227.       f = rnd(8) & 7;
  1228.     else if(id & BOMB)
  1229.       f = rnd(2) & 1;
  1230.     grid[dx][dy] = id | (f << 29); /* OR-in new rotation bits */
  1231.     hyper_in(dx, dy);              /* Fade in the piece (ADVGRAPH.C) */
  1232.   }
  1233.   else  /* If landed on a closed (empty) grid gate... */
  1234.   {
  1235.     if(grid[x][y] & KING)
  1236.       end_game = grid[x][y];
  1237.     loss[loss_index++] = grid[x][y];
  1238.     explode_info[0].x = dx;
  1239.     explode_info[0].y = dy;
  1240.     explode_info[0].flag = TRUE;
  1241.     explode();                     /* Destroy the piece. */
  1242.     if(end_game != 0L)
  1243.       game_end(end_game);
  1244.   }
  1245. }
  1246.  
  1247.  
  1248. /* Returns FALSE if selected piece is out of range of */
  1249. /* point (dx, dy) or there is no unblocked path. */
  1250. within_range(dx, dy)
  1251. register int dx, dy;
  1252. {
  1253.   int rx, ry, rt, rf;
  1254.  
  1255.   rx = abs(dx - selected_x);  /* Horizontal distance */
  1256.   ry = abs(dy - selected_y);  /* Vertical distance */
  1257.   rt = rx + ry;      /* Summation of distance */
  1258.  
  1259.   /* Can't move more than the number of remaining moves */
  1260.   if(rt > moves_left - moves_taken)
  1261.   {
  1262.     return(FALSE);
  1263.   }
  1264.  
  1265.   switch(rt)
  1266.   {
  1267.     case 1: return(TRUE); break; /* Return a TRUE for adjacent squares. */
  1268.     case 2: rf = check_rone(dx, dy); break;
  1269.     case 3: rf = check_rtwo(dx, dy); break;
  1270.   }
  1271.  
  1272.   if(rf == FALSE)    /* If the path is blocked... */
  1273.     return(FALSE);
  1274.  
  1275.   /* A clear path within range must exist at this point, so return a TRUE. */
  1276.   return(TRUE);
  1277. }
  1278.  
  1279.  
  1280.  
  1281. check_rone(dx, dy) /* Checks for clear path from selected piece to (x, y) */
  1282. register int dx, dy; /* to (dx, dy).  Returns FALSE if doesn't exist. */
  1283. {
  1284.   int xdir, ydir;
  1285.  
  1286.   xdir = SIGN(dx - selected_x);
  1287.   ydir = SIGN(dy - selected_y);
  1288.  
  1289.   /* If there is something blocking an adjacent path (above, below,
  1290.      left or right; not diagonal). */
  1291.   if(grid[selected_x + xdir][selected_y] != 0L &&
  1292.   grid[selected_x][selected_y + ydir] != 0L)
  1293.     return(FALSE);
  1294.  
  1295.   return(TRUE);
  1296. }
  1297.  
  1298.  
  1299.  
  1300. /* Checks for clear path 3 squares away. Returns FALSE if none. */
  1301. check_rtwo(dx, dy)
  1302. register int dx, dy;
  1303. {
  1304.   int xdir, ydir, rx, ry;
  1305.  
  1306.   if(!check_rone(dx, dy))
  1307.     return(FALSE);     /* Check all range-1 squares */
  1308.  
  1309.   xdir = SIGN(dx - selected_x);
  1310.   ydir = SIGN(dy - selected_y);
  1311.   rx = abs(selected_x - dx);   /* Horizontal distance */
  1312.   ry = abs(selected_y - dy);   /* Vertical distance */
  1313.  
  1314.   if(dy == selected_y)    /* If aligned horizontally with destination... */
  1315.   {
  1316.     /* If there is something 2 squares away... */
  1317.     if(grid[selected_x + xdir * 2][selected_y] != 0L)
  1318.       return(FALSE);
  1319.   }
  1320.  
  1321.   if(dx == selected_x)    /* If aligned vertically... */
  1322.   {
  1323.     /* If something between... */
  1324.     if(grid[selected_x][selected_y + ydir * 2] != 0L)
  1325.       return(FALSE);
  1326.   }
  1327.  
  1328.   if(ry == 1)  /* If y destination only one square above or below... */
  1329.   {
  1330.     /* If something blocks horizontally... */
  1331.     if(grid[selected_x + xdir * 2][selected_y] != 0L)
  1332.       /* If something blocks diagonally... */
  1333.       if(grid[selected_x + xdir][selected_y + ydir] != 0L)
  1334.         return(FALSE);
  1335.  
  1336.     if(grid[selected_x + xdir][selected_y + ydir] != 0L)
  1337.       if(grid[selected_x + xdir][selected_y] != 0L)
  1338.         return(FALSE);
  1339.   }
  1340.  
  1341.   else if(ry == 2)  /* If y destination two squares above or below... */
  1342.   {
  1343.     /* If something blocks vertically and diagonally... */
  1344.     if(grid[selected_x][selected_y + ydir * 2] != 0L)
  1345.       if(grid[selected_x + xdir][selected_y + ydir] != 0L)
  1346.         return(FALSE);
  1347.  
  1348.     if(grid[selected_x][selected_y + ydir] != 0L &&
  1349.     grid[selected_x + xdir][selected_y + ydir] != 0L)
  1350.       return(FALSE);
  1351.   }
  1352.  
  1353.   return(TRUE);     /* Otherwise, all clear; send a TRUE. */
  1354. }
  1355.  
  1356.  
  1357.  
  1358. /* Carries out explosions for all pieces marked in the explosion array */
  1359. explode()
  1360. {
  1361.   int explpx[EXPPTS], explpy[EXPPTS], explxv[EXPPTS], explyv[EXPPTS];
  1362.   register int i, j, num_exp, d;
  1363.   BOOL king_hit = FALSE;
  1364.  
  1365.   num_exp = 0;           /* Keeps track of the number of explosions */
  1366.   for(i=0; i<MAXEXPLOSIONS; i++) /* Count number of explosions */
  1367.     if(explode_info[i].flag)
  1368.       ++num_exp;
  1369.   if(num_exp == 0) return; /* Return if no explosions */
  1370.  
  1371.   for(i=0; i<num_exp; i++)  /* Initialize all points */
  1372.     if(explode_info[i].flag) /* If this piece is marked to be destroyed... */
  1373.     {
  1374.       /* Erase exploding piece */
  1375.       draw_square(explode_info[i].x, explode_info[i].y);
  1376.  
  1377.       /* Set the number of points for each exploding piece inversely
  1378.        * proportional to the number of pieces exploding. */
  1379.       for(j=0; j<EXPPTS / num_exp; j++)
  1380.       {
  1381.         d = (i * EXPPTS / num_exp) + j;
  1382.         explpx[d] = GRIDLEFT + (explode_info[i].x * GRIDSQUAREWIDTH) +
  1383.           rnd(GRIDSQUAREWIDTH);
  1384.         explpy[d] = GRIDTOP + (explode_info[i].y * GRIDSQUAREHEIGHT) +
  1385.           rnd(GRIDSQUAREHEIGHT);
  1386.         explxv[d] = rnd(EXPLOSIONMAXXV) - EXPLOSIONMAXXV / 2;
  1387.         explyv[d] = rnd(EXPLOSIONMAXYV) - EXPLOSIONMAXYV / 2;
  1388.         if(explxv[d] == 0 && explyv[d] == 0)
  1389.           explxv[d] = explyv[d] = rnd(5) + 1;
  1390.       }
  1391.       /* Record loss and erase piece from grid memory */
  1392.       if(grid[explode_info[i].x][explode_info[i].y] != OTHER)
  1393.       {
  1394.         if(grid[explode_info[i].x][explode_info[i].y] & KING)
  1395.           king_hit = TRUE;
  1396.         loss[loss_index++] = grid[explode_info[i].x][explode_info[i].y];
  1397.         grid[explode_info[i].x][explode_info[i].y] = 0L;
  1398.       }
  1399.     }
  1400.  
  1401.   explode_sound();
  1402.   if(king_hit)
  1403.   {
  1404.     DisplayBeep(screen);
  1405.     DisplayBeep(screen);
  1406.   }
  1407.   animate_explosion(num_exp, explpx, explpy, explxv, explyv);
  1408.   for(i=0; i<MAXEXPLOSIONS; i++)
  1409.     explode_info[i].flag = FALSE; /* Mark all explosions as inactive */
  1410. }
  1411.  
  1412.  
  1413.  
  1414. game_end(id)        /* Ends the game; requires victim piece ID */
  1415. unsigned long id;
  1416. {
  1417.   int i, j;
  1418.  
  1419.   end_game_display(id); /* Send VICTIM'S identification code */
  1420.  
  1421.   /* Mask off ALIVE bit of every piece to disable them. */
  1422.   /* This way, the players can still use the icon functions, but can't */
  1423.   /* do a thing with the pieces on the board. */
  1424.   for(i=0; i<GRIDX; i++)
  1425.     for(j=0; j<GRIDY; j++)
  1426.       grid[i][j] = grid[i][j] & 0xFFFFFFFE;
  1427. }
  1428.  
  1429.  
  1430.  
  1431. save_or_restore()
  1432. {
  1433.   register int action;
  1434.  
  1435.   draw_open_disk();
  1436.   copy_screen(current_bitmap, &backup_bitmap);  /* Save current screen */
  1437.   action = save_restore_prompt();
  1438.   copy_screen(&backup_bitmap, current_bitmap);  /* Erase prompt */
  1439.   if(action == RESTORE) restore_game();
  1440.   else if(action == SAVE) save_game();
  1441.   else draw_closed_disk(TRUE);
  1442. }
  1443.  
  1444.  
  1445.  
  1446. save_game()
  1447. {
  1448.   if(get_filename(SAVE))
  1449.   {
  1450.     strcpy(default_game, game_name);  /* Renew default game name */
  1451.     save_game_data();
  1452.     draw_closed_disk(TRUE);
  1453.   }
  1454.   else draw_closed_disk(TRUE);
  1455. }
  1456.  
  1457.  
  1458.  
  1459. restore_game()
  1460. {
  1461.   if(get_filename(RESTORE))
  1462.   {
  1463.     strcpy(default_game, game_name);  /* Renew default game name */
  1464.     if(load_game_data(!QUIET))
  1465.     {
  1466.       draw_closed_disk(TRUE);
  1467.       init_game();
  1468.       fade();
  1469.     }
  1470.     else draw_closed_disk(TRUE);
  1471.   }
  1472.   else draw_closed_disk(TRUE);
  1473. }
  1474.  
  1475.  
  1476.  
  1477. /* Lets the user enter in a path/file name for a file to save/restore */
  1478. get_filename(action)
  1479. register int action;
  1480. {
  1481.   char undo[GAMENAMELENGTH];
  1482.   static struct StringInfo strinfo =  /* String information definition */
  1483.   {
  1484.     NULL,                        /* Buffer - set below */
  1485.     NULL,                        /* UndoBuffer - set below */
  1486.     0,                           /* BufferPos */
  1487.     GAMENAMELENGTH - 1,          /* MaxLength */
  1488.     0, 0,                        /* DispPos, UndoPos */
  1489.     0, 0, 0, 0, NULL, NULL, NULL /* Intuition-handled variables */
  1490.   };
  1491.   static struct Gadget gadget =  /* String gadget definition */
  1492.   {
  1493.     NULL,                        /* NextGadget */
  1494.     SCREENX / 2 - GETNAME_BOX_WIDTH / 2 + 5,  /* LeftEdge */
  1495.     SCREENY / 2 - GETNAME_BOX_HEIGHT / 2 + 5, /* TopEdge */
  1496.     GETNAME_BOX_WIDTH - 5, 8,    /* Width, Height */
  1497.     SELECTED | GADGHCOMP,        /* Flags */
  1498.     NULL,                        /* Activation */
  1499.     STRGADGET,                   /* GadgetType */
  1500.     NULL, NULL,                  /* GadgetRender, SelectRender */
  1501.     NULL,                        /* GadgetText */
  1502.     NULL,                        /* MutualExclude */
  1503.     (APTR)&strinfo,              /* SpecialInfo */
  1504.     NULL,                        /* GadgetID */
  1505.     NULL                         /* UserData */
  1506.   };
  1507.  
  1508.   if(action != SAVE && action != RESTORE) return;
  1509.  
  1510.   copy_screen(current_bitmap, &backup_bitmap);
  1511.   set_color(FADE_COLOR, COLOR_GETNAME_BOX);
  1512.   draw_box(GETNAME_BOX_WIDTH, GETNAME_BOX_HEIGHT, FADE_COLOR);
  1513.   if(action == SAVE)  /* Transfer SAVE icon */
  1514.   {
  1515.     BltBitMap(&image_bitmap, (long)SP_SR_ICONSETX1,
  1516.       (long)(SP_SR_ICONSETY1 + SR_ICON_HEIGHT + 1),
  1517.       current_bitmap, (long)GETNAME_SRX1, (long)GETNAME_SRY1,
  1518.       (long)SR_ICON_WIDTH, (long)SR_ICON_HEIGHT, BLIT_11_MINTERM, -1L, NULL);
  1519.   }
  1520.   else  /* Transfer LOAD icon */
  1521.   {
  1522.     BltBitMap(&image_bitmap, (long)SP_SR_ICONSETX1, (long)SP_SR_ICONSETY1,
  1523.       current_bitmap, (long)GETNAME_SRX1, (long)GETNAME_SRY1,
  1524.       (long)SR_ICON_WIDTH, (long)SR_ICON_HEIGHT, BLIT_11_MINTERM, -1L, NULL);
  1525.   }
  1526.   /* Transfer CANCEL icon */
  1527.   BltBitMap(&image_bitmap, (long)SP_SR_ICONSETX1,
  1528.     (long)(SP_SR_ICONSETY1 + SR_ICON_HEIGHT * 2 + 2),
  1529.     current_bitmap, (long)GETNAME_CANCELX1, (long)GETNAME_CANCELY1,
  1530.     (long)SR_ICON_WIDTH, (long)SR_ICON_HEIGHT, BLIT_11_MINTERM, -1L, NULL);
  1531.  
  1532.   strinfo.Buffer = (UBYTE *)game_name;
  1533.   strinfo.UndoBuffer = (UBYTE *)undo;
  1534.   strcpy(undo, game_name);
  1535.   AddGadget(window, &gadget, -1L);
  1536.   RefreshGadgets(&gadget, window, NULL);
  1537.   ActivateGadget(&gadget, window, NULL);
  1538.   action = monitor_getname_icons();
  1539.   RemoveGadget(window, &gadget);
  1540.   copy_screen(&backup_bitmap, current_bitmap);
  1541.   return(action);
  1542. }
  1543.  
  1544.  
  1545.  
  1546. /* Returns TRUE if the SAVE/LOAD icon was hit, FALSE if CANCEL was hit. */
  1547. monitor_getname_icons()
  1548. {
  1549.   int class, code, qualifier, x, y;
  1550.  
  1551.   dequeue_window();  /* Remove all messages waiting at IDCMP */
  1552.   while(TRUE)
  1553.   {
  1554.     monitor_window_idcmp(&class, &code, &qualifier, &x, &y);
  1555.     if(class == MOUSEBUTTONS && (code == SELECTDOWN || code == MENUDOWN))
  1556.     {
  1557.       if(x >= GETNAME_SRX1 && x <= GETNAME_SRX1 + SR_ICON_WIDTH)
  1558.         if(y >= GETNAME_SRY1 && y <= GETNAME_SRY1 + SR_ICON_HEIGHT - 1)
  1559.           return(TRUE);
  1560.       if(x >= GETNAME_CANCELX1 && x <= GETNAME_CANCELX1 + SR_ICON_WIDTH)
  1561.         if(y >= GETNAME_CANCELY1 && y <= GETNAME_CANCELY1 + SR_ICON_HEIGHT - 1)
  1562.           return(FALSE);
  1563.     }
  1564.   }
  1565. }
  1566.  
  1567.  
  1568.  
  1569. /* Returns SAVE, RESTORE, or CANCEL. */
  1570. save_restore_prompt()  /* Prompts user for save or restore operation */
  1571. {
  1572.   static struct IntuiText itext =
  1573.   {
  1574.     WHITE, 1,    /* FrontPen, BackPen */
  1575.     JAM1,    /* Drawing mode */
  1576.     0, 0,    /* LeftEdge, TopEdge */
  1577.     &font,   /* ITextFont */
  1578.     (UBYTE *)NULL, /* Text (filled in below) */
  1579.     NULL     /* NextText */
  1580.   };
  1581.   int action;
  1582.  
  1583.   set_color(FADE_COLOR, COLOR_SR_BOX);  /* Draw the save/restore box */
  1584.   draw_box(SR_BOX_WIDTH, SR_BOX_HEIGHT, FADE_COLOR);
  1585.   /* Draw save/load/cancel icon set */
  1586.   BltBitMap(&image_bitmap, (long)SP_SR_ICONSETX1, (long)SP_SR_ICONSETY1,
  1587.     current_bitmap, (long)(SCREENX / 2 - SR_ICONSET_WIDTH / 2),
  1588.     (long)(SCREENY / 2 - SR_ICONSET_HEIGHT / 2), (long)SR_ICONSET_WIDTH,
  1589.     (long)(SR_ICONSET_HEIGHT + 1), BLIT_11_MINTERM, -1L, NULL);
  1590.   action = monitor_sr_icons();
  1591.   return(action);
  1592. }
  1593.  
  1594.  
  1595.  
  1596. monitor_sr_icons()  /* Monitors save/restore/cancel icon set */
  1597. {
  1598.   int class, code, qualifier, x, y, action = -1;
  1599.  
  1600.   dequeue_window();  /* Remove all messages waiting at IDCMP */
  1601.   while(action != SAVE && action != RESTORE && action != CANCEL)
  1602.   {
  1603.     monitor_window_idcmp(&class, &code, &qualifier, &x, &y);
  1604.     if(class == MOUSEBUTTONS && (code == SELECTDOWN || code == MENUDOWN))
  1605.       if(x >= SCREENX / 2 - SR_ICONSET_WIDTH / 2 &&
  1606.       x <= SCREENX / 2 + SR_ICONSET_WIDTH / 2)
  1607.         if(y >= SCREENY / 2 - SR_ICONSET_HEIGHT / 2 &&
  1608.         y <= SCREENY / 2 + SR_ICONSET_HEIGHT / 2)
  1609.         {
  1610.           y -= (SCREENY / 2 - SR_ICONSET_HEIGHT / 2);
  1611.           if(y < SR_ICON_HEIGHT - 1)  /* If loading... */
  1612.             action = RESTORE;
  1613.           else if(y < SR_ICON_HEIGHT * 2 && y > SR_ICON_HEIGHT)
  1614.             action = SAVE;
  1615.           else if(y < SR_ICON_HEIGHT * 3 + 1 && y > SR_ICON_HEIGHT * 2 + 1)
  1616.             action = CANCEL;
  1617.         }
  1618.   }
  1619.   return(action);
  1620. }
  1621.  
  1622.  
  1623.  
  1624. monitor_window_idcmp(class, code, qualifier, x, y)
  1625. int *class, *code, *qualifier, *x, *y;
  1626. {
  1627.   struct IntuiMessage *msg;
  1628.  
  1629.   WaitPort(window->UserPort);
  1630.   msg = (struct IntuiMessage *)GetMsg(window->UserPort);
  1631.   *class = msg->Class;
  1632.   *code = msg->Code;
  1633.   *qualifier = msg->Qualifier;
  1634.   *x = msg->MouseX;
  1635.   *y = msg->MouseY;
  1636.   ReplyMsg(msg);
  1637. }
  1638.  
  1639.  
  1640.  
  1641. init_game()
  1642. {
  1643.   register int i;
  1644.  
  1645.   dequeue_window();     /* Remove all messages from window's IDCMP */
  1646.   draw_moves(moves_left - moves_taken);
  1647.   draw_grid();
  1648.   if(hyper_up) up_hyper();
  1649.   /* Renew the user-selected filename to the name of the game just loaded
  1650.      (always default_game). */
  1651.   strcpy(game_name, default_game);
  1652. }
  1653.  
  1654.  
  1655.  
  1656. save_game_data()
  1657. {
  1658.   FILE *file;
  1659.   struct game_format *game;
  1660.   static char *okay = "Ok";
  1661.   static char *nomem[] =
  1662.   {
  1663.     "A bit more memory is needed to",
  1664.     "save the game.  Close some",
  1665.     "existing programs or delete",
  1666.     "files from RAM, then try saving",
  1667.     "the game again."
  1668.   };
  1669.   static char *noopen[] =
  1670.   {
  1671.     "The file specified to save the",
  1672.     "game cannot be opened."
  1673.   };
  1674.   static char *nowrite[] =
  1675.   {
  1676.     "There was a problem attempting",
  1677.     "to save this game; the save file",
  1678.     "is not complete."
  1679.   };
  1680.  
  1681.   game = allocate_mem((long)sizeof(struct game_format),
  1682.     MEMF_PUBLIC | MEMF_CLEAR);
  1683.   if(game == NULL)
  1684.   {
  1685.     req(nomem, okay, okay, 5, TRUE);
  1686.     return;
  1687.   }
  1688.  
  1689.   digest_game(game, SAVE);
  1690.  
  1691.   file = fopen(default_game, "w");
  1692.   if(file == NULL)    /* Open for write-only */
  1693.   {
  1694.     FreeMem(game, (long)sizeof(struct game_format));
  1695.     req(noopen, okay, okay, 2, TRUE);
  1696.     return;
  1697.   }
  1698.  
  1699.   if(fwrite(game, 1, sizeof(struct game_format), file) <
  1700.   sizeof(struct game_format))  /* If wrote less than size of buffer... */
  1701.     req(nowrite, okay, okay, 3, TRUE);
  1702.  
  1703.   fclose(file);
  1704.   FreeMem(game, (long)sizeof(struct game_format));
  1705. }
  1706.  
  1707.  
  1708.  
  1709. load_game_data(quiet)  /* Returns TRUE if successful, else FALSE. */
  1710. register int quiet;  /* Reports no errors if quiet = QUIET */
  1711. {
  1712.   FILE *file;
  1713.   struct game_format *game;
  1714.   static char *okay = "Ok";
  1715.   static char *nomem[] =
  1716.   {
  1717.     "A bit more memory is needed to",
  1718.     "load the game.  Close some",
  1719.     "existing programs or delete some",
  1720.     "files from RAM, then try loading",
  1721.     "the game again."
  1722.   };
  1723.   static char *noopen[] = {"That file cannot be opened."};
  1724.   static char *noread[] =
  1725.   {
  1726.     "There is a problem in attempting",
  1727.     "to read that file."
  1728.   };
  1729.  
  1730.   game = allocate_mem((long)sizeof(struct game_format),
  1731.     MEMF_PUBLIC | MEMF_CLEAR);
  1732.   if(game == NULL)
  1733.   {
  1734.     if(!quiet) req(nomem, okay, okay, 5, TRUE);
  1735.     return(FALSE);
  1736.   }
  1737.  
  1738.   file = fopen(default_game, "r");
  1739.   if(file == NULL)    /* Open for read-only */
  1740.   {
  1741.     if(!quiet) req(noopen, okay, okay, 1, TRUE);
  1742.     return(FALSE);
  1743.   }
  1744.  
  1745.   if(fread(game, 1, sizeof(struct game_format), file) <
  1746.   sizeof(struct game_format))
  1747.   {
  1748.     if(!quiet) req(noread, okay, okay, 2, TRUE);
  1749.     FreeMem(game, (long)sizeof(struct game_format));
  1750.     fclose(file);
  1751.     return(FALSE);
  1752.   }
  1753.  
  1754.   fclose(file);
  1755.   digest_game(game, RESTORE);
  1756.   FreeMem(game, (long)sizeof(struct game_format));
  1757.   return(TRUE);
  1758. }
  1759.  
  1760.  
  1761.  
  1762. /* Displays an auto requester with the specified message data; it returns
  1763.    the value returned by the AutoRequest() function. */
  1764. req(body, pos, neg, lines, redraw_border)
  1765. register char **body, *pos, *neg;
  1766. int lines, redraw_border;
  1767. {
  1768.   int i, itlength = 0, d, result;
  1769.   ULONG IDCMPsave;
  1770.   struct IntuiText btext[MAX_LINES];
  1771.   static struct IntuiText ptext =   /* Positive button text */
  1772.   {
  1773.     0, 1,               /* FrontPen, BackPen */
  1774.     JAM1,               /* DrawMode */
  1775.     6, 3,               /* LeftEdge, TopEdge */
  1776.     &font,              /* ITextFont */
  1777.     NULL,               /* IText (set later) */
  1778.     NULL                /* NextText */
  1779.   };
  1780.   static struct IntuiText ntext =   /* Negative button text */
  1781.   {
  1782.     0, 1,               /* FrontPen, BackPen */
  1783.     JAM1,               /* DrawMode */
  1784.     6, 3,               /* LeftEdge, TopEdge */
  1785.     &font,              /* ITextFont */
  1786.     NULL,               /* IText (set later) */
  1787.     NULL                /* NextText */
  1788.   };
  1789.   static char *free_mem[] =
  1790.   {
  1791.     "You MUST free as much",
  1792.     "memory as possible",
  1793.     "before you can continue!"
  1794.   };
  1795.  
  1796.   /* Init button text */
  1797.   ptext.IText = (UBYTE *)pos;
  1798.   ntext.IText = (UBYTE *)neg;
  1799.  
  1800.   /* The IDCMP flags are saved, then set to NULL in
  1801.      the window structure. This prevents any messages being
  1802.      sent to the window while the requester is on the
  1803.      screen. This is done because the program cannot
  1804.      reply to the incoming messages while the requester
  1805.      is out, which causes additional memory to be allocated
  1806.      for the incoming messages. The IDCMP flags will be
  1807.      returned to their original state below. */
  1808.   if(window != NULL)
  1809.   {
  1810.     IDCMPsave = window->IDCMPFlags;
  1811.     ModifyIDCMP(window, NULL);
  1812.   }
  1813.  
  1814.   /* Initialize all IntuiText structures pertaining to
  1815.      the body text of the requester... */
  1816.   for(i=0; i<lines; i++)
  1817.   {
  1818.     btext[i].FrontPen = 0;
  1819.     btext[i].BackPen = 1;
  1820.     btext[i].DrawMode = JAM1;
  1821.     btext[i].LeftEdge = 10;
  1822.     btext[i].TopEdge = i * 10 + 5;
  1823.     btext[i].ITextFont = &font;
  1824.     btext[i].IText = (UBYTE *)body[i];
  1825.     if(i == lines - 1) btext[i].NextText = NULL;
  1826.     else btext[i].NextText = &btext[i + 1];
  1827.   }
  1828.  
  1829.   if(IntuitionBase != NULL)  /* Avoid if Intuition library isn't loaded */
  1830.   {
  1831.     /* Find the longest line (in pixels)- this is used
  1832.        to construct the requester width */
  1833.     for(i=0; i<lines; i++)
  1834.       if((d = IntuiTextLength(&btext[i])) > itlength)
  1835.         itlength = d;
  1836.     itlength += 42;  /* Give some additional space */
  1837.     if(itlength > SCREENX) itlength = SCREENX - 1;
  1838.  
  1839.     /* Deallocate all the memory used in the backup bitmap before the
  1840.     AutoRequest() requester is brought up to ensure enough memory is
  1841.     available.  The AutoRequest() function does not take kindly to
  1842.     low memory situations. */
  1843.     for(i=0; i<PLANES; i++)
  1844.       if(backup_bitmap.Planes[i] != NULL)
  1845.       {
  1846.         FreeMem(backup_bitmap.Planes[i], (long)(SCREENX / 8 * SCREENY));
  1847.         backup_bitmap.Planes[i] = NULL;
  1848.       }
  1849.  
  1850.     /* Note: if window is NULL (which means the backdrop window has not
  1851.     been successfully opened, the AutoRequest function will bring up
  1852.     the requester in the Workbench screen as a system request. */
  1853.     result = AutoRequest(window, btext, &ptext, &ntext, NULL, NULL,
  1854.       (ULONG)(itlength), (ULONG)(((lines + 1) * 11) + 50));
  1855.  
  1856.     if(!get_bitmap(&backup_bitmap))
  1857.       req(free_mem, "Ok", "Ok", 3);
  1858.   }
  1859.   else  /* Attempt to output to CLI */
  1860.   {
  1861.     for(i=0; i<lines; i++)
  1862.       printf("%s\n", body[i]);
  1863.   }
  1864.  
  1865.   if(window != NULL)
  1866.   {
  1867.     ModifyIDCMP(window, IDCMPsave); /* Return IDCMP flags */
  1868.     if(redraw_border)
  1869.       draw_border();  /* Redraw border because of requester interference */
  1870.   }
  1871.   return(result);
  1872. }
  1873.  
  1874.  
  1875.  
  1876. /* Transfers data from the game format structure to the global variables
  1877.    or visa versa */
  1878. digest_game(game, action)
  1879. register struct game_format *game;
  1880. register int action;
  1881. {
  1882.   register int x, y;
  1883.  
  1884.   if(action == SAVE)
  1885.   {
  1886.     game->turn = turn;
  1887.     game->moves_left = moves_left;
  1888.     game->moves_taken = moves_taken;
  1889.     game->hyper_up = hyper_up;
  1890.     game->selected_x = selected_x;
  1891.     game->selected_y = selected_y;
  1892.     game->selected_f = selected_f;
  1893.     game->select_flag = select_flag;
  1894.     game->end_game = end_game;
  1895.     game->loss_index = loss_index;
  1896.     for(x=0; x<4; x++)
  1897.       game->gate[x] = gate[x];
  1898.     for(x=0; x<GRIDX; x++)
  1899.       for(y=0; y<GRIDY; y++)
  1900.         game->grid[x][y] = grid[x][y];
  1901.     for(x=0; x<GRIDX * GRIDY; x++)
  1902.       game->loss[x] = loss[x];
  1903.   }
  1904.   else
  1905.   {
  1906.     turn = game->turn;
  1907.     moves_left = game->moves_left;
  1908.     moves_taken = game->moves_taken;
  1909.     hyper_up = game->hyper_up;
  1910.     selected_x = game->selected_x;
  1911.     selected_y = game->selected_y;
  1912.     selected_f = game->selected_f;
  1913.     select_flag = game->select_flag;
  1914.     end_game = game->end_game;
  1915.     loss_index = game->loss_index;
  1916.     for(x=0; x<4; x++)
  1917.       gate[x] = game->gate[x];
  1918.     for(x=0; x<GRIDX; x++)
  1919.       for(y=0; y<GRIDY; y++)
  1920.         grid[x][y] = game->grid[x][y];
  1921.     for(x=0; x<GRIDX * GRIDY; x++)
  1922.       loss[x] = game->loss[x];
  1923.   }
  1924. }
  1925.  
  1926.  
  1927.  
  1928. dequeue_window()
  1929. {
  1930.   struct IntuiMessage *dqmsg;
  1931.  
  1932.   /* Reply to all pending messages in backdrop window's IDCMP */
  1933.   while((dqmsg = (struct IntuiMessage *)GetMsg(window->UserPort)))
  1934.     ReplyMsg(dqmsg);
  1935. }
  1936.  
  1937.  
  1938.  
  1939. void *allocate_mem(size, requirements)
  1940. ULONG size, requirements;
  1941. {
  1942.   /* Do not permit memory to go below the number defined by LOW_MEMORY_LIMIT.
  1943.      This prevents the system from getting too low on memory, which is just
  1944.      asking for trouble. */
  1945.   if(AvailMem(requirements) - size <= LOW_MEMORY_LIMIT)
  1946.     return(NULL);
  1947.  
  1948.   return(AllocMem(size, requirements));
  1949. }
  1950.  
  1951.  
  1952.  
  1953. int abs(value)
  1954. register int value;
  1955. {
  1956.   return(value < 0? -value : value);
  1957. }
  1958.  
  1959.  
  1960.  
  1961. int mouse_to_grid(mx, my, gx, gy)
  1962. register int mx, my, *gx, *gy;
  1963. {
  1964.   *gx = (mx - GRIDLEFT) / GRIDSQUAREWIDTH;
  1965.   *gy = (my - GRIDTOP) / GRIDSQUAREHEIGHT;
  1966. }
  1967.  
  1968.  
  1969.  
  1970. int rnd(range)
  1971. register int range;
  1972. {
  1973.   static long seed = -1L;
  1974.  
  1975.   seed += seed;
  1976.   if(seed < 0L) seed ^= 0x1d872b41;
  1977.   return(abs((int)seed % range));
  1978. }
  1979.  
  1980.  
  1981.